package com.doge.calcalbum.security;

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * http://www.cnblogs.com/whoislcj/p/5473030.html<br>
 * <pre>
 * 什么是aes加密？
 * 高级加密标准（英语：Advanced Encryption Standard，缩写：AES），在密码学中又称Rijndael加密法，是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES，已经被多方分析且广为全世界所使用。
 * </pre>
 */
public class AESUtils {
    private final static String HEX = "0123456789ABCDEF";
    private static final String SHA1PRNG = "SHA1PRNG";

    /*
     * 生成随机数，可以当做动态的密钥 加密和解密的密钥必须一致，不然将不能解密
     */
    public static String generateKey() {
        try {
            SecureRandom localSecureRandom = SecureRandom.getInstance(SHA1PRNG);
            byte[] bytes_key = new byte[20];
            localSecureRandom.nextBytes(bytes_key);
            return toHex(bytes_key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * 加密
     */
    public static String encrypt(String key, String cleartext) {
        if ("".equals(cleartext)) {
            return cleartext;
        }
        try {
            byte[] result = encrypt(key, cleartext.getBytes());
            return Base64Encoder.encode(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * 加密
     */
    private static byte[] encrypt(String key, byte[] clear) throws Exception {
        SecretKeySpec secretKeySpec = getKey(key);
        final byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        // 指定加密的算法、工作模式和填充方式
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(clear);
    }

    /*
     * 解密
     */
    public static String decrypt(String key, String encrypted) {
        if ("".equals(encrypted)) {
            return encrypted;
        }
        try {
            byte[] enc = Base64Decoder.decodeToBytes(encrypted);
            byte[] result = decrypt(key, enc);
            return new String(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * 解密
     */
    private static byte[] decrypt(String key, byte[] encrypted) throws Exception {
        SecretKeySpec secretKeySpec = getKey(key);
        final byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(encrypted);
    }

    private static SecretKeySpec getKey(String key) {
        int keyLength = 256;
        byte[] keyBytes = new byte[keyLength / 8];
        Arrays.fill(keyBytes, (byte) 0x0);
        byte[] passwordBytes = key.getBytes(StandardCharsets.UTF_8);
        int length = Math.min(passwordBytes.length, keyBytes.length);
        System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
        return new SecretKeySpec(keyBytes, "AES");
    }

    //二进制转字符
    public static String toHex(byte[] buf) {
        if (buf == null)
            return "";
        StringBuffer result = new StringBuffer(2 * buf.length);
        for (byte b : buf) {
            appendHex(result, b);
        }
        return result.toString();
    }

    private static void appendHex(StringBuffer sb, byte b) {
        sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
    }

    public static void main(String[] args) {
        String jsonData = "abcdefghijklmnopqrstuvwxyz";
        System.out.println("AESUtils jsonData ---->" + jsonData);
        System.out.println("AESUtils jsonData length ---->" + jsonData.length());

        //生成key
        String secretKey = AESUtils.generateKey();
        System.out.println("AESUtils secretKey ---->" + secretKey);

        //AES加密
        long start = System.currentTimeMillis();
        String encryStr = AESUtils.encrypt(secretKey, jsonData);
        long end = System.currentTimeMillis();
        System.out.println("AESUtils encrypt cost time---->" + (end - start));
        System.out.println("AESUtils jsonData(encrypted) ---->" + encryStr);
        System.out.println("AESUtils jsonData(encrypted) length ---->" + encryStr.length());

        //AES解密
        start = System.currentTimeMillis();
        String decryStr = AESUtils.decrypt(secretKey, encryStr);
        end = System.currentTimeMillis();
        System.out.println("AESUtils decrypt cost time---->" + (end - start));
        System.out.println("AESUtils jsonData(decrypted) ---->" + decryStr);
    }
}

